﻿using Cofe.Core;
using Cofe.Core.Implements;
using Cofe.Core.Logger;
using Cofe.Core.Property;
using Cofe.Core.Utils;
using Cofe.IO;
using Cofe.IO.Interfaces;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using CofeCore = Cofe.Core;
using CofeIO = Cofe.IO;

namespace Cofe.IO.Implements
{
#if !NETFX_CORE
    public class SystemIOPropertyProvider : PropertyProviderBase<FileSystemInfo>, IFSIPropertyProvider
    {
        #region Constructor

        public SystemIOPropertyProvider(SystemIODirectoryLister directoryLister, FileSystemInfo entry)
            : base(entry,
            entry is System.IO.DirectoryInfo ?
                       new IPropertyDefinitions[] { 
                CofeCore.PropertyDefinitions.CofeDefinitions, 
                CofeCore.PropertyDefinitions.CofeDirDefinitions, 
                CofeIO.PropertyDefinitions.FSIPropDefinitions, 
                CofeIO.PropertyDefinitions.DIPropDefinitions 
            } :
            new IPropertyDefinitions[] { 
                CofeCore.PropertyDefinitions.CofeDefinitions, 
                CofeCore.PropertyDefinitions.CofeFileDefinitions, 
                CofeCore.PropertyDefinitions.CofeStreamDefinitions,
                CofeIO.PropertyDefinitions.FSIPropDefinitions, 
                CofeIO.PropertyDefinitions.FIPropDefinitions
            
            })
        {            
            ManualEventSource = new DummyCofeSystemEventSource();

            _dirLister = directoryLister;
            var volumeInfo = directoryLister.Volume;
            
            if (!entry.FullName.StartsWith(directoryLister.VolumePath))
                throw new Exception("");
            _parseName = PathFE.FormatFullParseName(volumeInfo.VolumeId, entry.FullName
                    .Replace(directoryLister.VolumePath, "", StringComparison.InvariantCultureIgnoreCase)).TrimEnd('\\');
            _parentParseName = PathFE.GetDirectoryName(_parseName);

            _isFile = Entity is System.IO.FileInfo;
            _isFolder = !_isFile;

            registerProperties(CofeProperties.DefaultInterfaceType, () =>
                    _isFile ? typeof(IFileInfo) : typeof(IDirectoryInfo));
            registerProperties(CofeProperties.Type, () => _isFile ? FileTypes.File : FileTypes.Folder);
            registerProperties(CofeProperties.Label, () => Entity.Name);

            registerProperties(CofeProperties.IsExists, () => Entity.Exists);
            registerProperties(FileSystemInfoProperties.FileAttributes, () => (Cofe.IO.FileAttributes)(int)Entity.Attributes);
            registerProperties(CofeProperties.CofeAttributes, () =>
                {
                    return (Entity is System.IO.DirectoryInfo) ?
                        CofeAttributes.FileSystem | CofeAttributes.SubEntriesCanChange | CofeAttributes.Browsable :
                        CofeAttributes.FileSystem;
                }
            );

            registerProperties(CofeProperties.LastWriteTimeUtc, () => Entity.LastWriteTimeUtc,
                (pp) => { Entity.LastWriteTimeUtc = pp.ValueAsUtcDateTime; return true; });
            registerProperties(CofeProperties.CreationTimeUtc, () => Entity.CreationTimeUtc,
                (pp) => { Entity.CreationTimeUtc = pp.ValueAsUtcDateTime; return true; });

            registerProperties(CofeProperties.FullParseName, () => _parseName);
            registerProperties(CofeProperties.ParentParseName, () => _parentParseName ?? null);
            registerProperties(FileSystemInfoProperties.FullName, () => Entity.FullName);

            registerActionAsync(CofeProperties.DeleteAsync, (pm) => Task.Run(() =>
                { Entity.Delete(); pm.IsHandled = true; }));

            registerActionAsync(CofeProperties.RefreshAsync, (pm) => Task.Run(() =>
            { Entity.Refresh(); pm.IsHandled = true; }));

            if (_isFile)
            {                
                registerProperties(CofeItemProperties.Length, () => { return (Entity as System.IO.FileInfo).Length; });
                registerActionAsync(CofeStreamProperties.OpenReadAsync,
                    (pm) => Task.Run<StreamContainer>(() => StreamContainer.FromIOStream(new FileStream(Entity.FullName, FileMode.Open, FileAccess.Read), _parseName)));
                registerActionAsync(CofeStreamProperties.OpenReadWriteAsync,
                    (pm) => Task.Run<StreamContainer>(() => StreamContainer.FromIOStream(new FileStream(Entity.FullName, FileMode.OpenOrCreate, FileAccess.ReadWrite), _parseName)));
                registerActionAsync(CofeStreamProperties.OpenWriteAsync,
                    (pm) => Task.Run<StreamContainer>(() => StreamContainer.FromIOStream(new FileStream(Entity.FullName, FileMode.Create, FileAccess.Write), _parseName)));
            }

            if (_isFolder)
            {
                //Func<object[], Task<IEnumerable<IPropertyProvider>>>
                //    asyncList = (pm) => Task.Run<IEnumerable<IPropertyProvider>>(() => list());
                //registerProperties(CofeDirectoryProperties.ListAsync, () => asyncList);

                Func<ParameterDic, IEnumerable<IPropertyProvider>> syncList = (pmDic) => { pmDic.IsHandled = true; return list(); };                
                registerAction(CofeDirectoryProperties.ListCore, syncList);
                registerActionAsync(CofeDirectoryProperties.CreateAsync, (pm) => createAsync(pm));

                //registerProperties(CofeDirectoryProperties.DirectoryType, () =>
                //    {
                //        if (Entity.FullName.Equals(volumeInfo.VolumePath)) return DirectoryTypeEnum.dtVolume;
                //        return Entity.FullName.EndsWith(":\\") ? DirectoryTypeEnum.dtRoot : DirectoryTypeEnum.dtFolder;
                //    }
                //        );
                //registerProperties(CofeDirectoryProperties.IsFileBasedFS, () => false);
            }
        }

        #endregion

        #region Methods

        private IEnumerable<IPropertyProvider> list()
        {                        
            int ctr = 0;
            foreach (var fsi in DirEntity.EnumerateFileSystemInfos())
            {
                yield return new SystemIOPropertyProvider(_dirLister, fsi);
                ctr++;
            }
            CofeServices.Logger.Log("SystemIO listed {0} with {1} items", _parseName, ctr);
        }

        //private Task<bool> deleteAsync()
        //{

        //}

        private Task<IPropertyProvider> createAsync(ParameterDic dic)
        {
            return Task.Run<IPropertyProvider>(() =>
                {
                    
                    string type = dic.ContainsKey("Type") ? (string)dic["Type"] : null;
                    string name = dic.ContainsKey("Name") ? (string)dic["Name"] : null;                    

                    if (string.IsNullOrEmpty(type) || string.IsNullOrEmpty(name))
                        return null;
                    string destName = PathFE.Combine(Entity.FullName, name);
                    
                    CofeServices.Logger.Log("SystemIO creating {0}", destName);

                    switch (type.ToLower())
                    {
                        case "folder":
                        case "directory":
                            try
                            {
                                dic.IsHandled = true;
                                var createdDir = System.IO.Directory.CreateDirectory(destName);
                                ManualEventSource.NotifyCreated(PathFE.Combine(_parseName, name), true);
                                return new SystemIOPropertyProvider(_dirLister, createdDir);
                            }
                            catch (Exception ex)
                            {
                                dic.Error = ex;
                                return null;
                            }
                        case "file":
                            try
                            {
                                dic.IsHandled = true;
                                using (var s = System.IO.File.Create(destName)) { }
                                ManualEventSource.NotifyCreated(PathFE.Combine(_parseName, name), false);
                                return new SystemIOPropertyProvider(_dirLister, new System.IO.FileInfo(destName));
                            }
                            catch (Exception ex)
                            {
                                dic.Error = ex;
                                return null;
                            }
                        default:
                            return null;
                    }
                });
        }

        #endregion

        #region Data

        private SystemIODirectoryLister _dirLister;
        private string _parseName, _parentParseName;
        private bool _isFile, _isFolder;

        #endregion

        #region Public Properties

        public IManualCofeSystemEventSource ManualEventSource { get; set; }
        public System.IO.DirectoryInfo DirEntity { get { return Entity as System.IO.DirectoryInfo; } }
        public System.IO.FileInfo FileEntity { get { return Entity as System.IO.FileInfo; } }

        #endregion
    }
#endif
}
